#include "command.hpp"
#include <sstream>
#include <cstdio>

uint16_t readRange(uint16_t mask, uint16_t a)
{
	uint16_t t = GM.rw(a)&mask, m = mask;
	while (m%2 != 1)
	{
		m/=2;
		t/=2;
	}
	return GM.rw(a)&mask;
}
class MR
{
public:
	uint8_t m;
	uint8_t r;
	
	MR(uint8_t v): m(v/8), r(v%8) {assert(v <= 077);}
	int islong()
	{
		return (m == 6 || m == 7) ? 1 : 0;
	}
	string print()
	{
		stringstream ss;
		ss << (int)r;
		switch(m)
		{
			case 0:
				return (string("R") += (ss.str()));
			case 1:
				return (string("(R") += (ss.str()) += ")");
			case 2:
				return (string("(R") += (ss.str()) += "+)");
			case 3:
				return (string("@(R") += (ss.str()) += "+)");
			case 4:
				return (string("(-R") += (ss.str()) += ")");
			case 5:
				return (string("@(-R") += (ss.str()) += ")");
			case 6:
			case 7:
			{
				stringstream sss;
				sss << oct << GM.rw(PC);
				GM.ww(PC,GM.rw(PC)+2);
				return (string("") += string((m==7) ? "@" : "") += (sss.str())
					+= string("(R") += (ss.str()) += ")");
			}
			default:
				assert(0);
		}
	}
	int value(bool word)
	{
		int ret = 0;
		switch(m)
		{
			case 0:
				ret = REGISTER+r;
				break;
			case 1:
				ret = GM.rw(REGISTER+r);
				break;
			case 2:
				ret = GM.rw(REGISTER+r);
				GM.ww(REGISTER+r, GM.rw(REGISTER+r) + (word ? 2 : 1));
				break;
			case 3:
				ret = GM.rw(REGISTER+r);
				ret = GM.rw(ret);
				GM.ww(REGISTER+r, GM.rw(REGISTER+r) + 2);
				break;
			case 4:
				GM.ww(REGISTER+r, GM.rw(REGISTER+r) - (word ? 2 : 1));
				ret = GM.rw(REGISTER+r);
				break;
			case 5:
				GM.ww(REGISTER+r, GM.rw(REGISTER+r) - 2);
				ret = GM.rw(REGISTER+r);
				ret = GM.rw(ret);
				break;
			case 6:
			case 7:
				ret = GM.rw(PC);
				ret += GM.rw(REGISTER+r);
				GM.ww(PC,GM.rw(PC)+2);
				if(m == 7)
					ret = GM.rw(ret);
				break;
			default:
				assert(0);
		}
		return ret;
	}
};
//Single operand data analysis
void* SOpRead(uint16_t a)
{
	return new pair<bool,MR> (readRange(0100000, a),readRange(077, a));
}
string SOpPrettify(void *o)
{
	bool b = ((pair<bool,MR>*)o)->first;
	MR mr = ((pair<bool,MR>*)o)->second;
	return (string(b ? "B " : " ") += mr.print());
}
//Double operand data analysis
void* DOpRead(uint16_t a)
{
	return new pair<bool,pair<MR,MR>*> (readRange(0100000, a),new pair<MR,MR>(readRange(07700, a),readRange(077, a)));
}
string DOpPrettify(void *o)
{
	bool b = ((pair<bool,pair<MR,MR>*>*)o)->first;
	MR mr1 = (((pair<bool,pair<MR,MR>*>*)o)->second)->first;
	MR mr2 = (((pair<bool,pair<MR,MR>*>*)o)->second)->second;
	return (string(b ? "B " : " ") += mr1.print() += string(" ") += mr2.print());
}
//Offset data analysis
void* offsetRead(uint16_t a)
{
	return new int8_t(readRange(0377, a));
}
string offsetPrettify(void *o)
{
	stringstream ss;
	int v = (int)(uint8_t)*(int8_t *)o;
	ss << oct << ((v&0200) ? " -" : " ") << ((v&0200) ? ((v^0377)+1) : v);
	delete (int8_t *)o;
	return ss.str();
}


#define REGFUNC(name, ...) \
	void name(void *d) __VA_ARGS__

#if 1	//Single operand
#define SOPDATA\
	bool b = ((pair<bool, MR>*)d)->first;\
	int v = ((pair<bool, MR>*)d)->second.value(!b);
REGFUNC(CLR,
{SOPDATA
	fprintf(stderr,"[%d %o]\n",b,v);
	// cout << '\n' << (b==1?"bool":"word")<< '\n';
	if (b)	GM.wb(v,0);
	else 	GM.ww(v,0);
	GM.N = GM.V = GM.C = 0;
	GM.Z = 0;
})
REGFUNC(COM,
{SOPDATA
	if (b)	
	{
		GM.wb(v,GM.rb(v)^0000377);
		GM.Z = !GM.rb(v);
		GM.N =  GM.rb(v)&0000200;
	}
	else 
	{
		GM.ww(v,GM.rw(v)^0177777);
		GM.Z = !GM.rw(v);
		GM.N =  GM.rw(v)&0100000;
	}
	GM.V = 0;
	GM.C = 1;
})
REGFUNC(INC,
{SOPDATA
	if (b)	
	{
		GM.wb(v,GM.rb(v)+1);
		GM.Z = !GM.rb(v);
		GM.N = ((int)GM.rb(v) < 0);
		GM.V = (GM.rb(v) == 0000200);
	}
	else 
	{
		GM.ww(v,GM.rw(v)+1);
		GM.Z = !GM.rw(v);
		GM.N = ((int)GM.rw(v) < 0);
		GM.V = (GM.rb(v) == 0100000);
	}
})
REGFUNC(DEC,
{SOPDATA
		if (b)	
	{
		GM.wb(v,GM.rb(v)-1);
		GM.Z = !GM.rb(v);
		GM.N = ((int)GM.rb(v) < 0);
		GM.V = (GM.rb(v) == 0000177);
	}
	else 
	{
		GM.ww(v,GM.rw(v)-1);
		GM.Z = !GM.rw(v);
		GM.N = ((int)GM.rw(v) < 0);
		GM.V = (GM.rb(v) == 0077777);
	}
	GM.C = !GM.Z;
})
REGFUNC(NEG,
{SOPDATA
	//TODO: GM.V???
	//TODO: CHECK: 010000 -> 010000
	if (b)	
	{
		GM.wb(v,-((int8_t)GM.rb(v)));
		GM.Z = !GM.rb(v);
		GM.N = ((int)GM.rb(v) < 0);
	}
	else 
	{
		GM.ww(v,-((int16_t)GM.rw(v)));
		GM.Z = !GM.rw(v);
		GM.N = ((int)GM.rw(v) < 0);
	}
})
REGFUNC(TST,
{SOPDATA
	if (b)	
	{
		GM.Z = !GM.rb(v);
		GM.N = ((int)GM.rb(v) < 0);
	}
	else 
	{
		GM.Z = !GM.rw(v);
		GM.N = ((int)GM.rw(v) < 0);
	}
	GM.V = GM.C = 0;
})
#undef SOPDATA
#endif

#if 1	//Double operand
#define DOPDATA\
	bool b = ((pair<bool, MR>*)d)->first;\
	int v1 = (((pair<bool, pair<MR, MR>*>*)d)->second)->first.value(!b);\
	int v2 = (((pair<bool, pair<MR, MR>*>*)d)->second)->second.value(!b);
REGFUNC(MOV,
{DOPDATA
	if (b)
	{
		if(v2 < REGISTER)
			GM.wb(GM.rb(v2),GM.rb(v1));
		else
			GM.ww(GM.rb(v2),(int16_t)((int8_t)GM.rb(v1)));
		GM.N = (GM.rb(v1) < 0);
		GM.Z = (GM.rb(v1) == 0);
	}
	else
	{
		GM.ww(GM.rw(v2),GM.rw(v1));
		GM.N = (GM.rw(v1) < 0);
		GM.Z = (GM.rw(v1) == 0);
	}
	GM.V = 0;
})
#endif

#if 1	//Branch
REGFUNC(BR,
{
	int8_t o = *(int8_t*)d;
	uint16_t old = GM.rw(PC);
	assert((o >= 0 && old + 2*o >= old) || (o < 0 && old + 2*o < old));
	GM.ww(PC, old+2*o);
})

REGFUNC(BNE,
	{if(GM.Z == 0) BR(d);})
REGFUNC(BEQ,
	{if(GM.Z != 0) BR(d);})
REGFUNC(BPL,
	{if(GM.N == 0) BR(d);})
REGFUNC(BMI,
	{if(GM.N != 0) BR(d);})
REGFUNC(BVC,
	{if(GM.V == 0) BR(d);})
REGFUNC(BVS,
	{if(GM.V != 0) BR(d);})
REGFUNC(BCC,
	{if(GM.C == 0) BR(d);})
REGFUNC(BCS,
	{if(GM.C != 0) BR(d);})
	
REGFUNC(BGE,
	{if(!GM.N == !GM.V) BR(d);})
REGFUNC(BLT,
	{if(!GM.N != !GM.V) BR(d);})
REGFUNC(BGT,
	{if(!(GM.Z || (!GM.N == !GM.V))) BR(d);})
REGFUNC(BLE,
	{if(GM.Z || (!GM.N == !GM.V)) BR(d);})

REGFUNC(BHI,
	{if(GM.Z && GM.C) BR(d);})
REGFUNC(BLOS,
	{if(GM.Z || GM.C) BR(d);})
REGFUNC(BHIS,
	{BCC(d);})
REGFUNC(BLO,
	{BCS(d);})
#endif
#undef REGFUNC


void registerAllComands()
{
	
#define CMDREG(m,o,name,fR,fP)   registerCommand(Command(m, o, #name, name, fR, fP))
	CMDREG(0077700, 0005000,  CLR,    SOpRead,    SOpPrettify);
	CMDREG(0077700, 0005100,  COM,    SOpRead,    SOpPrettify);
	CMDREG(0077700, 0005200,  INC,    SOpRead,    SOpPrettify);
	CMDREG(0077700, 0005300,  DEC,    SOpRead,    SOpPrettify);
	CMDREG(0077700, 0005400,  NEG,    SOpRead,    SOpPrettify);
	CMDREG(0077700, 0005700,  TST,    SOpRead,    SOpPrettify);
	
	CMDREG(0070000, 0010000,  MOV,    DOpRead,    DOpPrettify);
	
	CMDREG(0177400, 0000400,   BR, offsetRead, offsetPrettify);
	
	CMDREG(0177400, 0001000,  BNE, offsetRead, offsetPrettify);
	CMDREG(0177400, 0001400,  BEQ, offsetRead, offsetPrettify);
	CMDREG(0177400, 0100000,  BPL, offsetRead, offsetPrettify);
	CMDREG(0177400, 0100400,  BMI, offsetRead, offsetPrettify);
	CMDREG(0177400, 0102000,  BVC, offsetRead, offsetPrettify);
	CMDREG(0177400, 0102400,  BVS, offsetRead, offsetPrettify);
	CMDREG(0177400, 0103000,  BCC, offsetRead, offsetPrettify);
	CMDREG(0177400, 0103400,  BCS, offsetRead, offsetPrettify);
	
	CMDREG(0177400, 0002000,  BGE, offsetRead, offsetPrettify);
	CMDREG(0177400, 0002400,  BLT, offsetRead, offsetPrettify);
	CMDREG(0177400, 0003000,  BGT, offsetRead, offsetPrettify);
	CMDREG(0177400, 0003400,  BLE, offsetRead, offsetPrettify);
	
	CMDREG(0177400, 0101000,  BHI, offsetRead, offsetPrettify);	
	CMDREG(0177400, 0101400, BLOS, offsetRead, offsetPrettify);
	CMDREG(0177400, 0103000, BHIS, offsetRead, offsetPrettify); //Duplicate~!
	CMDREG(0177400, 0103400,  BLO, offsetRead, offsetPrettify);	//Duplicate~!
#undef CMDREG
	
	cerr << oct;
	int x;
	for(int i = 0; i <= 07777; i++)
		GM.ww(i*2, i*4);
	for(int i = 0; i <= 7; i++)
		GM.ww(REGISTER + i, (i+1)*0100);
	x = 0;
	for(int i = 0; i <= 077; i++)
		cerr << MR(i).print() << ((i%8 == 7) ? '\n' : ' ');
	x = 0;
	for(int i = 0; i <= 077; i++)
		cerr  << MR(i).value(true) << ((i%8 == 7) ? '\n' : ' ');
	cerr << oct << x;
	
	for(int i = 0005000; i < 0005500; i++)
	{
		if((i&077)==057)
			continue;
		for(int j = 0; j <= MEM_SIZE/2; j++)
			GM.ww(j*2, j*4);
		for(int j = 0; j <= 7; j++)
			GM.ww(REGISTER + j, (j+1)*0100);
		GM.ww(0502, 0000010);
		GM.ww(0500, i);
		GM.ww(PC, 0500);
		printCommand(GM.rw(PC));
		for(int j = 0; j <= MEM_SIZE/2; j++)
			GM.ww(j*2, j*4);
		for(int j = 0; j <= 7; j++)
			GM.ww(REGISTER + j, (j+1)*0100);
		GM.ww(0502, 0000010);
		GM.ww(0500, i);
		GM.ww(PC, 0500);
		cerr << oct << "PC: " << GM.rw(PC) << " -> ";
		executeCommand(GM.rw(PC));
		cerr << oct << GM.rw(PC) << "\n";
	}
	GM.ww(0502, 0000010);
	GM.ww(PC, 0500);
	printCommand(GM.rw(PC));
		
	cerr << oct << "PC: " << GM.rw(PC) << " -> ";
	x = executeCommand(GM.rw(PC));
	GM.ww(PC, GM.rw(PC) + x);
	cerr << oct << GM.rw(PC) << "\n";

}